package javaParser;
//Abdulla Al-Ali
//modified 4/18/08



/***
 * This class stores a tree of all RdfClass that are in the project
 * use this to access all classes, methods, variables, fields, params.. etc
 * of the whole project
 */
import java.util.ArrayList;
import java.util.LinkedList;

public class RdfTree {

	private ArrayList<RdfClass> classes;
	private RdfClass currentClass;
	private RdfMethod currentMethod;
	
	/*** ctor requires no params
	 * 
	 */
	public RdfTree() {
		classes = new ArrayList<RdfClass>();
	}
	
	/*** add a class to the tree
	 * 
	 * @param classToAdd class to be added
	 */
	public void addClass(RdfClass classToAdd) {
		classes.add(classToAdd);
		currentClass = classToAdd;
	}
	
	/*** invoke this method after calling addClass
	 *   this will add the method to the current class
	 * 
	 * @param m RdfMethod that you want to add
	 */
	public void addMethod(RdfMethod method) {
		currentClass.addMethod(method);
		currentMethod = method;
	}
	
	/*** param is the method that is called,
	 *   use it when adding a calls relationship to the last added method
	 * 
	 * @param m
	 */
	public void addCalls(RdfMethod method) {
		currentMethod.addCalls(method);
	}
	
	
	/*** this will add a field to the current class
	 * 
	 * @param field
	 */
	public void addField(RdfField field) {
		currentClass.addField(field);
	}
	
	
	/***** Adding params to methods only, although addParam() is 
	 * accessible through RdfClass ?
	 * @param param RdfParameter that you want to add to the current
	 *              method
	 */
	public void addParam(RdfParam param) {
		if (currentMethod!=null)
		currentMethod.addParam(param);
	}
	
	/*** returns the size of the tree (i.e. all classes in it)
	 * 
	 * @return the number of classes in the tree
	 */
	public int size() {
		return classes.size();
	}
	
	/*** get an RdfClass by index
	 * 
	 * @param index the index to be added. must be an int
	 * @return returns the correspondent RdfClass
	 */
	public RdfClass get(int index) {
		return classes.get(index);
	}
	
	/*** get the current class (can help debugging)
	 * 
	 * @return the current RdfClass in the iteration
	 */
	public RdfClass getCurrentClass() {
		return currentClass;
	}
	
	
	/*** get last added method (can help debugging)
	 * 
	 * @return the current RdfMethod in the iteration
	 */
	public RdfMethod getCurrentMethod() {
		return currentMethod;
	}
	
	/*** retrieve a class by providing a class name only (string)
	 * 
	 * @param name a string that represents the classname.
	 * @return returns an RdfClass representing that string
	 */
	public RdfClass getClass(String name) {
		RdfClass result = null;
		for (int i=0; i<classes.size(); i++) {
			//System.out.println("class: " + classes.get(i).getName());
			if (classes.get(i).getName().equals(name)) {
				result = classes.get(i);
				break;
			}
		}
		return result;
	}
	
	/*** This method receives a dummy RdfMethod and returns 
	 *   the actual RdfMethod
	 *   question: why do we need to handle parameters list
	 *      if the real actual RdfMethod already has them ? the dummy method
	 *      must not hold any information other than the dummy method's name.
	 *  
	 * @param dummy the RdfMethod that is flagged as dummy
	 * @return RdfMethod that was added previously, null if not found
	 */
	public RdfMethod getMethod(RdfClass currentclass, RdfMethod dummy) {
		
		/*String temp = "";
		for (int i=0; i<dummy.getFullname().length; i++) {
			temp = temp + dummy.getFullname()[i] + ".";
		}
		System.out.println("Method: " + temp);*/
		
		RdfClass tempClass = null;
		RdfMethod tempMethod = null;
		RdfField tempField =  null;
		String tempName = "";
		String[] methodFullPath = dummy.getFullname();
		if (methodFullPath==null) {
			return null;
		}
		//store parent classes' name
		String parentClass = methodFullPath[0];
		//dummy method's name
		String dummyMethodName = "";
		if (methodFullPath.length == 1) {
			//length is 1, should not be trying to get real from dummy from here
			for (int i=0; i<currentclass.sizeOfMethods(); i++) {
				if (currentclass.getMethod(i).getName().equals(methodFullPath[0])) {
					return currentclass.getMethod(i);
				}
			}
		}
		else if (methodFullPath.length>1) {
			dummyMethodName = methodFullPath[1];
			tempClass = getClass(parentClass);
			if (tempClass == null) {
				//class not found
				return null;
			}
			if (methodFullPath.length==2) {
				return getMethodFromClass(tempClass, dummyMethodName);
			} else { //we have a nested loop
				tempName = methodFullPath[1];
				for (int i=1; i<methodFullPath.length-1; i++) {
					//check if its field
					tempField = this.getFieldFromClass(tempClass, tempName);
					if (tempField != null) {
						tempClass = this.getClass(tempField.getType());
						if (tempClass == null) return null;
						tempName = methodFullPath[i+1];
					} else { //check if its a method
						tempMethod = this.getMethodFromClass(tempClass, tempName);
						if (tempMethod != null) {
							tempClass = this.getClass(tempField.getType());
							tempName = methodFullPath[i+1];
							if (tempClass == null) return null;
							tempName = methodFullPath[i+1];
						} else { //its not a method nor a field
							
							return null;
						}
					}
				}
				//if all went well, the for loop ends here and we have a tempClass
				tempMethod = this.getMethodFromClass(tempClass, methodFullPath[methodFullPath.length-1]);
				if (tempMethod == null) return null;
				else return tempMethod;
			}
		}

		return null;
	}
	
	
	private RdfMethod getMethodFromClass(RdfClass rdfClass, String methodName) {
		for (int i=0; i<rdfClass.sizeOfMethods(); i++) {
			if (methodName == rdfClass.getMethod(i).getName()) {
				return rdfClass.getMethod(i);
			}
		}
		return null;
	}
	
	private RdfField getFieldFromClass(RdfClass rdfClass, String fieldName) {
		LinkedList<RdfField> list = rdfClass.getFields();
		for (int i=0; i<list.size(); i++) {
			if (fieldName == list.get(i).getName()) {
				return list.get(i);
			}
		}
		return null;
	}
	
	
}
